Skip to content

Add North Dakota renter's refund#8314

Merged
daphnehanse11 merged 7 commits into
PolicyEngine:mainfrom
daphnehanse11:codex/nd-renters-refund
May 21, 2026
Merged

Add North Dakota renter's refund#8314
daphnehanse11 merged 7 commits into
PolicyEngine:mainfrom
daphnehanse11:codex/nd-renters-refund

Conversation

@daphnehanse11
Copy link
Copy Markdown
Member

@daphnehanse11 daphnehanse11 commented May 14, 2026

Summary

  • Adds North Dakota's Renter's Refund for age-65-or-permanently-and-totally-disabled renters.
  • Models the renter refund formula from Tax Commissioner guidance: 20% of eligible annual rent less 4% of income, with a $5 minimum qualifying refund and a $600 cap.
  • Adds focused eligibility and refund amount tests, including the state example, cap, ineligible filer, permanent-disability path, and minimum-refund edge case.

Closes #8199.

Homeowner follow-up: #8325 tracks the separate North Dakota Homestead Property Tax Credit model.

Value Review

  • The Tax Commissioner page sets eligibility at age 65 or older, or permanent and total disability.
  • The Tax Commissioner page limits renter income to $70,000, including spouse and dependent income for the calendar year preceding the assessment date, and says renters have no asset limitation.
  • The Tax Commissioner page says renters are eligible if 20% of annual rent exceeds 4% of annual income, with refunds up to $600.
  • The Tax Commissioner guideline says qualifying applicant refunds may not exceed $600 and will be at least $5.

Modeling Notes

  • This PR models the renter refund only. The homeowner Homestead Property Tax Credit reduces taxable value rather than issuing a direct renter-style refund, and is tracked in Model North Dakota Homestead Property Tax Credit #8325.
  • The model uses adjusted_gross_income as the closest available proxy for the program's income-from-all-sources measure after eligible medical expense deductions.
  • The model uses rent as eligible rent, assuming it excludes utilities, furniture, garage rent, federal rent subsidy, and rent paid for property-tax-exempt quarters.
  • The model uses is_permanently_and_totally_disabled for the disability eligibility path.

Sources:

Tests

  • /Users/daphnehansell/Documents/GitHub/policyengine-us/.venv/bin/ruff check policyengine_us/variables/gov/states/nd/tax/property/renters_refund policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund
  • /Users/daphnehansell/Documents/GitHub/policyengine-us/.venv/bin/python -m policyengine_core.scripts.policyengine_command test policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund -c policyengine_us
  • /Users/daphnehansell/Documents/GitHub/policyengine-us/.venv/bin/python -m pytest policyengine_us/tests/test_parameter_files.py policyengine_us/tests/test_system_import.py -q

@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (bf8b00a) to head (8f458cd).
⚠️ Report is 35 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##             main     #8314       +/-   ##
============================================
+ Coverage   78.70%   100.00%   +21.29%     
============================================
  Files        4750         4     -4746     
  Lines       69270        52    -69218     
  Branches      341         0      -341     
============================================
- Hits        54517        52    -54465     
+ Misses      14675         0    -14675     
+ Partials       78         0       -78     
Flag Coverage Δ
unittests 100.00% <100.00%> (+21.29%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@daphnehanse11 daphnehanse11 marked this pull request as draft May 14, 2026 20:29
@daphnehanse11 daphnehanse11 marked this pull request as ready for review May 20, 2026 19:04
@daphnehanse11 daphnehanse11 requested a review from hua7450 May 20, 2026 19:04
@hua7450
Copy link
Copy Markdown
Collaborator

hua7450 commented May 20, 2026

Program Review (read-only audit)

Source Documents


Critical (Must Fix)

  1. income_limit.yaml effective date does not match real-life policy historypolicyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_limit.yaml. The $70,000 cap took effect for taxable years beginning after 2022-12-31 (HB 1158, 2023 §§ 1 & 8), not 2025. Pre-2023 cap was $42,000. The current single entry 2025-01-01: 70_000 backward-extrapolates $70,000 to every prior year (including pre-2023, where the real-life cap was $42,000). Fix: change 2025-01-01: 70_0002023-01-01: 70_000, and add 2003-01-01: 42_000 (or wherever the program's intended start year is) so the parameter reflects the actual policy history. Source: HB 1158 §1 (income brackets) + HB 1158 §8 (effective date — TY after 2022-12-31).

  2. cap.yaml missing pre-2025 $400 entry — does not match real-life policy historypolicyengine_us/parameters/gov/states/nd/tax/property/renters_refund/cap.yaml. The $400 cap was set by the 2009 ND legislative session (verified against ND Tax Commissioner Property Tax History: "2009 Session — Increased the maximum refund to $400") and remained in force through 2024. HB 1176 §§ 8 & 31 raised it to $600 effective for taxable years after 2024-12-31. The current single entry 2025-01-01: 600 backward-extrapolates $600 to every pre-2025 year, when the real-life cap was $400. Fix: add 2009-01-01: 400 before the 2025-01-01: 600 entry. Source: ND HB 1176 fiscal note ("from a maximum refund amount of $400 to $600 annually") + ND Tax Commissioner Property Tax History.

  3. income_sources.yaml includes child_support_receivedpolicyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_sources.yaml:9. Guideline 24887 Item 11 (PDF 1 p.2) explicitly lists "Child support" under "Items which are not income for purposes of homestead credit." Fix: remove child_support_received.

  4. income_sources.yaml includes survivor_benefitspolicyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_sources.yaml:11. PE's survivor_benefits variable is documented as "Survivor benefits other than Social Security survivor benefits" (i.e., workers-comp / pension survivors). Guideline 24887 Item 11 explicitly excludes "Workers compensation payments, including survivor benefits." SS survivors are already captured by social_security. Fix: remove survivor_benefits.

  5. income_sources.yaml includes financial_assistancepolicyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_sources.yaml:10. PE's variable is "Cash financial assistance from outside the household" — semantically equivalent to gifts/cash transfers, which Guideline Item 11 excludes ("Money received from gifts or inheritance") and which IRC §102 excludes from federal gross income (falling under the statute's "any amount excluded from income by federal or state law" carve-out). Fix: remove financial_assistance.


Should Address

  1. ssi in income_sources.yaml — statute is ambiguous, recommend resolvingpolicyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_sources.yaml:7. N.D.C.C. § 57-02-08.1(5)(c) has two clauses in tension:

    • Includes "any county, state, or federal public assistance benefits" → would include SSI
    • Excludes "any amount excluded from income by federal or state law with the exception of income from social security benefits" → SSI is federally excluded from gross income (42 U.S.C. § 1382), so falls under this carve-out
      Guideline 24887 does not explicitly list SSI in either Item 10 (income) or Item 11 (not income), but Item 11 excludes other non-taxable federal benefits (TANF, food stamps, VA disability, federal fuel assistance), suggesting SSI is excluded by analogy under the "not limited to" wording. Recommend: decide explicitly. The conservative (and more common-state) reading is to exclude SSI.
  2. Generic landing-page references on 4 parameter filesage_threshold.yaml, income_limit.yaml, income_rate.yaml, rent_rate.yaml cite only https://www.tax.nd.gov/renters-refund (a navigation page). Fix: add statute § 57-02-08.1 (https://ndlegis.gov/cencode/t57c02.pdf#page=15 / #page=16) and Guideline 24887 page 7 (items 36, 37, 45) as primary references.

  3. minimum.yaml missing statute citation — add N.D.C.C. § 57-02-08.1(2)(b) (https://ndlegis.gov/cencode/t57c02.pdf#page=16) as the statutory authority for the $5 floor.

  4. cap.yaml missing codified-statute citation alongside session law — add N.D.C.C. § 57-02-08.1(2)(b) (https://ndlegis.gov/cencode/t57c02.pdf#page=16) as the codified authority for the $600 amount.

  5. Variable-level reference fields use only the landing page on nd_renters_refund.py, nd_renters_refund_eligible.py, nd_renters_refund_property_tax_exempt.py. Fix: replace with statute § 57-02-08.1 + Guideline 24887 page 7 (and § 57-02-08.1(2)(e) for the property-tax-exempt variable).

  6. Utilities-included rent not modeled (rent-base utility offset)variables/gov/states/nd/tax/property/renters_refund/nd_renters_refund.py. Statute § 57-02-08.1(2)(b) and Form 24757 require subtracting landlord-paid utilities (14% heat, 2% water/garbage, 6% electric) and furniture from gross rent before applying 20%. The formula uses rent directly. Fix: add an inline comment documenting "we don't track utilities-included rent separately at the moment" as a known modeling limitation.

  7. Property-tax-exempt input variable lacks documentationvariables/gov/states/nd/tax/property/renters_refund/nd_renters_refund_property_tax_exempt.py. Variable defaults to False for the entire microsim population, meaning the model gives the refund to renters of public-housing / tax-exempt facilities, which violates statute § 57-02-08.1(2)(e). Fix: add a docstring citing § 57-02-08.1(2)(e) and noting that the input defaults to False and represents the public-housing / nursing-home carve-out (which is not tracked from upstream inputs at the moment).

  8. Redundant outer max_(..., 0) in benefit formulavariables/gov/states/nd/tax/property/renters_refund/nd_renters_refund.py:15-20. Eligibility (defined_for) already requires the inner subtraction to be strictly positive (nd_renters_refund_eligible.py:29), so the outer max_(..., 0) is dead defensive code. Fix: simplify to refund = rent_share - income_share; keep the min_(p.cap, max_(p.minimum, refund)) outer wrapping.

  9. Redundant (rent > 0) check in eligibilityvariables/gov/states/nd/tax/property/renters_refund/nd_renters_refund_eligible.py:28. The condition (rent * p.rent_rate - income * p.income_rate) > 0 already implies rent > 0 when income is non-negative (which nd_renters_refund_income enforces via max_(...,0)). Fix: remove the (rent > 0) clause.

  10. At-threshold income test missingtests/.../nd_renters_refund_eligible.yaml. Formula uses <= income_limit; only 70_001 (just over) is tested. Fix: add a case with nd_renters_refund_income: 70_000 expecting nd_renters_refund_eligible: true.

  11. No multi-person tax unit test — all cases are single-filer. head_or_spouse filter and add(tax_unit, ["rent"]) aggregation are untested for married/dependent scenarios. Fix: add a married-couple-both-65+ case.

  12. Income-sources coverage incompletetests/.../nd_renters_refund_income.yaml covers only market_income, social_security, medical expenses, and dependent earnings. Fix: after the income-source list is corrected (criticals 3-5 and SSI decision), add a case with non-zero unemployment_compensation to verify list completeness.

  13. No integration-from-inputs refund test — every refund case pre-supplies nd_renters_refund_income. Fix: add at least one case that derives income from employment_income/social_security upstream.

  14. No property-tax-exempt refund test — exemption is tested only at the eligibility level. Fix: add a refund test case with nd_renters_refund_property_tax_exempt: true expecting nd_renters_refund: 0.

  15. No rent == 0 test — eligibility formula has & (rent > 0). Fix: add a senior-with-no-rent case expecting nd_renters_refund_eligible: false.

  16. head_or_spouse filter narrower than statute — undocumentedvariables/gov/states/nd/tax/property/renters_refund/nd_renters_refund_eligible.py:18-20. Statute § 57-02-08.1(1)(a) does not require the eligible person to be the tax-unit head/spouse. Fix: add a docstring noting that eligibility is approximated by is_tax_unit_head_or_spouse per the form's applicant model.


Suggestions

  1. Cap-yaml reference titles could name the statute being amended — "HB 1176 § 8" → "HB 1176 § 8 — amending N.D.C.C. § 57-02-08.1(2)(b), raising cap from $400 to $600."
  2. Redundant period.this_year on age accessor in YEAR formuland_renters_refund_eligible.py:16. period.this_year is a no-op in a YEAR formula; just use period.
  3. Parameter description style — consider formal program name — Form 24757 uses "Senior Citizens or Permanently and Totally Disabled Renter's Property Tax Refund." Either name is defensible.
  4. Test case 7 in nd_renters_refund_eligible.yaml named misleadingly — Case 7 ("taxable and nontaxable Social Security income counts") re-uses Case 3's income-too-high scenario and sets no social-security inputs.
  5. Consider extracting rent * p.rent_rate - income * p.income_rate into an intermediate variable — both nd_renters_refund.py and nd_renters_refund_eligible.py compute the same expression.
  6. Reference-tuple style consistencynd_renters_refund_income.py uses a tuple for reference; other variables use a single string.

PDF Audit Summary

Category Count
Confirmed correct 11
Mismatches (verified) 5
Mismatches downgraded to ambiguous (SSI) 1
Mismatches rejected (code-path cleared) 2
Unmodeled items (known limitations) 5

Confirmed correct: age_threshold (65), income_limit value ($70,000 — value correct, effective date wrong), rent_rate (0.20), income_rate (0.04), cap value ($600 for 2025), minimum ($5), formula structure (max(0, 0.2·rent − 0.04·income) clamped to [5, 600]), inclusion of market_income / social_security / unemployment_compensation / general_assistance in income sources, medical expense subtraction via itemized_medical_expenses.

Mismatches rejected (code-path cleared): general_assistance (statute explicitly includes "county, state, or federal public assistance benefits" and Guideline does not list state GA as excluded); itemized_medical_expenses is the correct medical-expense variable (pre-AGI floor, IRC §213(d) — switching to medical_expense_deduction would silently apply the 7.5% AGI floor that ND doesn't apply).

Unmodeled items (out-of-scope or known limitations): one-refund-per-household rule (§ 57-02-08.1(2)(c)), June 1 filing deadline, deceased-applicant estate filing, utilities-in-rent offset (Form 24757 lines 2a-2d), mobile-home-on-rented-lot exception.


Validation Summary

Check Result
Regulatory Accuracy 3 critical / 4 should-address
Reference Quality 0 critical / 4 should-address
Code Patterns 0 critical / 2 should-address / 3 suggestions
Test Coverage 0 critical / 6 gaps
PDF Value Audit 5 mismatches / 11 confirmed
CI Status not checked in this review

Review Severity: REQUEST_CHANGES

The 5 confirmed criticals (2 date-history gaps + 3 income-source list entries) are all backed by primary-source statutory or guideline text. The SSI question is ambiguous in the statute and should be resolved one way or the other by the author.


Review generated by /review-program (read-only). To auto-fix issues: /fix-pr 8314.

Copy link
Copy Markdown
Collaborator

@hua7450 hua7450 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes based on the full review in this comment.

5 critical (must fix):

  1. income_limit.yaml — effective date should be 2023-01-01: 70_000 (HB 1158), with <earlier>: 42_000 for the prior era.
  2. cap.yaml — add 2009-01-01: 400 before the 2025-01-01: 600 entry.
  3. Remove child_support_received from income_sources.yaml (Guideline 24887 Item 11 exclusion).
  4. Remove survivor_benefits from income_sources.yaml (workers-comp survivor — Guideline Item 11).
  5. Remove financial_assistance from income_sources.yaml (gifts/inheritance — Guideline Item 11 + IRC §102).

Plus one statutory ambiguity to resolve (ssi) and 15 should-address items in the full review comment.

@daphnehanse11 daphnehanse11 merged commit cbd97b9 into PolicyEngine:main May 21, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add North Dakota Homestead Property Tax Credit and Renter's Refund

2 participants